iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0

目前的登入頁面是跳轉到 Keycloak 預設的登入頁,所以會出現樣式跟 NextJs App 不一致的情形,keycloak 本身是有自訂樣式的功能,但與 React 大相徑庭,要調成一致不知道要費多少工。

幸好有工具可以處理這個問題,Keycloakify 提供使用 React 做成 Keycloak theme 的功能,能夠使用 tailwind, styledComponent 等樣式工具,也能用 Mui 等元件庫來製作。

首先將 keycloakify 專案複製到本地並安裝

git clone https://github.com/keycloakify/keycloakify-starter
cd keycloakify-starter
yarn install

再來為了開發方便,先開啟 storybook 好看到改動後的樣子。

一開始還沒有任何 story ,先用指令選擇想要顯示在 storybook 中的頁面,再開啟 storybook,這邊以 login 頁面為例

npx keycloakify add-story
npm run storybook

https://res.cloudinary.com/dhcsjvhjg/image/upload/v1726834566/Screenshot_2024-09-20_at_8.15.51_PM_engy7i.png

https://res.cloudinary.com/dhcsjvhjg/image/upload/v1726834828/Screenshot_2024-09-20_at_8.20.06_PM_zrj6tl.png

再來如果想要能夠整個元件做替換的話,要先將頁面 eject 後才能進行編輯。

npx keycloakify eject-page

https://res.cloudinary.com/dhcsjvhjg/image/upload/v1726834957/Screenshot_2024-09-20_at_8.22.28_PM_xcknes.png

執行後就會出現一個檔案 src/login/pages/Login.tsx 可以編輯來改動 login 頁面。

另外選完後會看到提示要將自訂頁面元件做切替。

// src/login/KcPage.tsx
// ...

+const Login = lazy(() => import("./pages/Login"));

 export default function KcPage(props: { kcContext: KcContext; }) {

     // ...

     return (
         <Suspense>
             {(() => {
                 switch (kcContext.pageId) {
                     // ...
+                    case "login.ftl": return (
+                        <Login
+                            {...{ kcContext, i18n, classes }}
+                            Template={Template}
+                            doUseDefaultCss={true}
+                        />
+                    );
                     default: return <Fallback /* .. */ />;
                 }
             })()}
         </Suspense>
     );
 }

接著先安裝 Mui

yarn add @mui/material @emotion/react @emotion/styled

來試著將 Sign in 按鈕替換成 Mui 元件,先找到原本的按鈕部分

<input
    tabIndex={7}
    disabled={isLoginButtonDisabled}
    className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
    name="login"
    id="kc-login"
    type="submit"
    value={msgStr("doLogIn")}
/>

替換成

<Button 
	variant="contained" 
	disabled={isLoginButtonDisabled} 
	name="login" 
	id="kc-login" 
	type="submit" 
	fullWidth
>
	  {msgStr("doLogIn")}
</Button>

就能看到按鈕變成 Mui 的樣子了。

https://res.cloudinary.com/dhcsjvhjg/image/upload/v1726836439/Screenshot_2024-09-20_at_8.47.09_PM_zbrb15.png

移除背景

目前看到的背景圖是 body 跟 html 標籤上的 class 的影響,不在 login 頁面的範圍內,所以要從更上層的元件來修改。

先呈現修改後的結果

// src/login/KcPage.tsx
import { Suspense, lazy } from "react";
import type { ClassKey } from "keycloakify/login";
import type { KcContext } from "./KcContext";
import { useI18n } from "./i18n";
import DefaultPage from "keycloakify/login/DefaultPage";
import Template from "keycloakify/login/Template";
import { createTheme, ThemeProvider } from "@mui/material";
import { tss } from "tss-react/mui";

const UserProfileFormFields = lazy(
    () => import("keycloakify/login/UserProfileFormFields")
);
const Login = lazy(() => import("./pages/Login"));

const doMakeUserConfirmPassword = true;

const theme = createTheme({
    palette: {
        mode: "dark"
    }
});

export default function KcPage(props: { kcContext: KcContext }) {
    return (
        <ThemeProvider theme={theme}>
            <KcPageContext {...props} />
        </ThemeProvider>
    );
}

function KcPageContext(props: { kcContext: KcContext }) {
    const { kcContext } = props;

    const { i18n } = useI18n({ kcContext });

    const { classes } = useStyles();

    return (
        <Suspense>
            {(() => {
                switch (kcContext.pageId) {
                    case "login.ftl":
                        return (
                            <Login
                                {...{ kcContext, i18n, classes }}
                                Template={Template}
                                doUseDefaultCss={true}
                            />
                        );
                    default:
                        return (
                            <DefaultPage
                                kcContext={kcContext}
                                i18n={i18n}
                                classes={classes}
                                Template={Template}
                                doUseDefaultCss={true}
                                UserProfileFormFields={UserProfileFormFields}
                                doMakeUserConfirmPassword={doMakeUserConfirmPassword}
                            />
                        );
                }
            })()}
        </Suspense>
    );
}

const useStyles = tss.create(
    ({ theme }) =>
        ({
            kcHtmlClass: {},
            kcBodyClass: {
                backgroundColor: theme.palette.background.default
            }
        }) satisfies { [key in ClassKey]?: unknown }
);

主要是要將 kcHtmlClass, kcBodyClass 這兩個 class 清空或覆寫。

先到這邊,接著再來將結果打包後套用回 Keycloak。


上一篇
同步登出 NextJs 跟 Keycloak
下一篇
套用 Keycloakify 樣式到 Keycloak
系列文
Awesome self hosted 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言